/**
* 标准收发器
*/

#pragma once

#include <stdlib.h>
#include <stdbool.h>
#include "cccompat.h"
#include "std_err.h"

// 接收数据标志类型
typedef enum _rx_flag {
	RX_FLAG_NULL   = -2,    // 标志无效
	RX_FLAG_REGEXP = -1,    // 标志是正则表达式
	RX_FLAG_STRING = 0,     // 标志是普通字符串
	RX_FLAG_BINARY = 1,     // >=1 表示标志是二进制串，其值表示二进制串长度
} rx_flag_t;

// 接收数据函数返回值类型
typedef enum _rx_ret {
	RX_RET_ERROR    = -1,   // <0 表示接收出现内部错误，其值是 std_err_t
	RX_RET_FULL     =  0,   // 表示缓冲区满
	RX_RET_TIMEOUT      ,   // 表示总时间超时
	RX_RET_INTERVAL     ,   // 表示间隔时间超时
	RX_RET_OK           ,   // 表示匹配了 okFlag
	RX_RET_FAIL         ,   // 表示匹配了 failFlag
	RX_RET_ALL          ,   // 表示已返回当前缓冲区中所有数据
	RX_RET_MAX
} rx_ret_t;

// forward declares
typedef struct _transceiver transceiver_t;

/**
 * 抽象的数据收发器
 * 目标是统一各种不同的接口，比如 UART, TCP/UDP, PCIE, USB 等，以便应用层屏蔽底层通信接口差异
 */
typedef struct _transceiver {
	/**
	 * @brief 由驱动提供的指向驱动的底层数据结构的指针，具体由驱动自行定义（一般保存了设备的配置、状态等，对应用层屏蔽实现细节）
	 */
	void *hif;

	/**
	 * @brief 由驱动提供的函数: 获取收发器最大传输单元
	 * @param trx 收发器指针(相当于 this 指针)
	 */
	size_t (*get_mtu)(transceiver_t *trx);

	/**
	 * @brief 由驱动提供的函数: 清除接收缓冲区
	 * @param trx 收发器指针(相当于 this 指针)
	 * @return 正常返回 STD_ERR_OK/0，异常时返回错误码。
	 */
	std_err_t (*clear_rx_buffer)(transceiver_t *trx);

	/**
	 * @brief 由驱动提供的函数: 阻塞式接收数据
	 * @param trx 收发器指针(相当于 this 指针)
	 * @param buf 应用程序提供的接收缓冲区指针，大小由 size 指示
	 * @param size 最大接收数据大小。数据缓冲区满之后，应立即返回 RX_RET_FULL
	 * @param len [out] 实际接收到的数据大小，不应超过 size 大小；可以为 NULL，表示实际接收到的数据大小由其它途径获取(如字符串长度)
	 * @param timeout 总超时(ms)。-1 表示永远等待。超时后，不管接收到多少数据都应返回 RX_RET_TIMEOUT
	 * @param lead 前导超时(ms)，表示第一个字节到来之前等待超时时间。超时后，RX_RET_LEAD
	 * @param interval 间隔超时(ms)，表示底层两个接收数据片之间最大的间隔。超时后，不管接收到多少数据都应返回 RX_RET_INTERVAL
	 * @param okFlag 正常返回时的标志。具体类型由 okFlagType 决定。匹配到此标志之后，应返回 RX_RET_OK
	 * @param okFlagType 表示存放在 okFlag 中的具体标志类型。@see rx_flag_t
	 * @param failFlag 异常返回时的标志。具体类型由 failFlagType 决定。匹配到此标志之后，应返回 RX_RET_FAIL
	 * @param failFlagType 表示存放在 failFlag 中的具体标志类型。@see rx_flag_t
	 * @return 一个 rx_ret_t 型整数，表示接收结束的原因。@see rx_ret_t
	 */
	rx_ret_t (*rx)(transceiver_t *trx, void* buf, size_t size, size_t *len, long timeout, /*long lead,*/ long interval, const char *okFlag, rx_flag_t okFlagType, const char *failFlag, rx_flag_t failFlagType);

	/**
	 * @brief 由驱动提供的函数: 接收一行文本数据的函数（以 "\n" 表示一行结束）
	 * @param trx 收发器指针(相当于 this 指针)
	 * @param buf 应用程序提供的接收缓冲区指针，大小由 size 指示
	 * @param size 最大接收数据大小。数据缓冲区满之后，应立即返回 RX_RET_FULL
	 * @param len [out] 实际接收到的数据大小，不应超过 size 大小；可以为 NULL，表示实际接收到的数据大小由其它途径获取(如字符串长度)
	 * @return 正常返回 STD_ERR_OK/0，异常时返回错误码。
	 */
	std_err_t (*rxline)(transceiver_t *trx, void* buf, size_t size, size_t *len);

	/**
	 * @brief 由驱动提供的函数: 发送数据
	 * @param trx 收发器指针(相当于 this 指针)
	 * @param buf 应用程序提供的发送缓冲区指针，大小由 size 指示
	 * @param size 要发送的数据大小
	 * @param len [out] 实际发送的数据大小；可以为 NULL，表示不关心
	 * @return 正常返回 STD_ERR_OK/0，异常时返回错误码。
	 */
	std_err_t (*tx)(transceiver_t *trx, const void* buf, size_t size, size_t *len);

	/**
	 * @brief 由驱动提供的函数: 关闭收发器，释放相关资源
	 * @param trx 收发器指针(相当于 this 指针)
	 */
	void (*close)(transceiver_t *trx);

	/**
	 * @brief 由应用层提供的回调函数: 当驱动接收到一个 slice 数据时会回调此函数，告诉应用层收到了多少数据
	 * @param cookie 由应用层提供的 on_rx_data_cookie
	 * @param trx 收发器指针(相当于 this 指针)
	 * @param buf （在调用 rx() 时应用程序提供的）接收缓冲区指针，大小由 size 指示
	 * @param size （在调用 rx() 时应用程序提供的）接收缓冲区大小
	 * @param len 当前已经接收到的总数据大小（仅当 buf 有效时有效，包含本次 slice 的 size 在内）
	 * @param slice 本次 slice 的数据指针
	 * @param slice_size 本次 slice 的数据大小
	 * @attention 本回调函数有两种应用场景：
	 *   - 在调用 rx() 的同时，挂接了回调函数，此时 rx() 的内部分多次（每次称为一个 "slice"）收到数据，每次都进行回调。此时 buf, size 是由 rx() 传入的参数。
	 *   - 未调用 rx() 的时候，挂接了回调函数，此时底层由中断或其它方式收到数据（也称为一个 "slice"），进行回调。此时 buf, size, len 无意义，应传入空值。
	 */
	void (*on_rx_data)(void *cookie, transceiver_t *trx, void* buf, size_t size, size_t len, void* slice, size_t slice_size);
	void *on_rx_data_cookie;

	/**
	 * @brief 由应用层提供的回调函数: 当驱动发送数据缓冲区数据发送完成时会回调此函数
	 * @param cookie 由应用层提供的 on_tx_empty_cookie
	 * @param trx 收发器指针(相当于 this 指针)
	 * @param buf 应用程序提供的发送缓冲区指针，大小由 size 指示
	 * @param size 实际发送的数据大小
	 */
	void (*on_tx_empty)(void *cookie, transceiver_t *trx, void* buf, size_t size);
	void *on_tx_empty_cookie;

	/**
	 * @brief 由驱动提供的函数: 启动异步监听。要想使用 on_rx_data/on_tx_empty 事件回调，必须先启动异步监听。
	 * @param trx 收发器指针(相当于 this 指针)
	 */
	std_err_t (*listen)(transceiver_t *trx, bool enable);

	/**
	 * @brief 由应用层提供的回调函数: 用于正则表达式匹配字符串，如果没有此函数，则 rx() 不支持正则表达式匹配
	 * @param str 要匹配的字符串
	 * @param pattern 正则表达式
	 * @return >=0 第一个匹配的位置；<0 匹配失败
	 */
	int (*regexp_match)(const char* str, const char* pattern);

	/**
	 * @brief 由驱动维护的 引用计数。创建时为 1，每次重复打开时加 1，每次关闭时减 1，减到 0 时才真正关闭
	 */
	int ref_count;
} transceiver_t;


#ifdef __cplusplus
extern "C" {
#endif

/**
 * 正则表达式匹配字符串函数指针声明
 */
typedef int (*regexp_match_t)(const char* str, const char* regexp);

/**
 * 数据传输进度回调函数原型
 * @param cookie 回调函数挂接时提供的参数指针
 * @param total 总大小
 * @param transfered 已经传输的大小
 */
typedef void (*progress_callback_t)(transceiver_t* dev, void *cookie, size_t total, size_t transfered);

/**
 * 把 rx_ret_t 返回码翻译成 std_err_t 错误码(假定仅 RX_RET_OK 为 STD_ERR_OK)
 */
std_err_t rx_ret_to_std_err(rx_ret_t ret);

/**
 * 接收数据函数返回值对应的错误信息
 */
const char* rx_ret_comment(rx_ret_t ret);

/**
 * 打开一个 dummy 仿真收发器
 */
transceiver_t* transceiver_open_dummy(const char* addr);

/**
 * 判断收发器是否是一个 dummy 仿真收发器
 */
static inline bool transceiver_is_dummy(void *_trx) {
	struct _transceiver* trx = (struct _transceiver*)_trx;
	return (trx && (trx->hif == trx));
}

/**
 * 打开一个命名的数据收发器
 * @param addr 接口地址。这是一个类似于 url 地址规范的字符串，常见的接口及地址格式如：
 *   "uart://0?baudrate=115200&databits=8&stopbits=1&parity=n" 表示串口 UART0,115200,8,n,1
 *   "xdma://1234:4567/mm" 表示 XDMA-PCIE 接口，vid=0x1234，did=0x4567，memory-map 通道
 *   "xdma://1234:4567/lite" 表示 XDMA-PCIE 接口，vid=0x1234，did=0x4567，axi-lite 通道
 *   "tcp://192.168.0.1:1800" 表示 TCP Socket 客户端连接，对端 ip=192.168.0.1，port=1800
 * @return 收发器接口指针
 */
//transceiver_t* transceiver_open(const char* addr);

/**
 * 发送命令并接收回应（完成一次“命令-响应”式交互）
 * @param trx 驱动接口设备指针，具体含义由驱动自行决定
 * @param cmd 要发送的字符串格式的命令
 * @param rxbuf 应用程序提供的接收缓冲区指针，大小由 rxbuf_size 指示。实际接收的数据长度可由 strlen(rxbuf) 来判断。
 * @param rxbuf_size 最大接收数据大小。数据缓冲区满之后，应立即返回 RX_RET_FULL
 * @param timeout 总超时(ms)。-1 表示永远等待。超时后，不管接收到多少数据都应返回 RX_RET_TIMEOUT
 * @param okFlag 正常返回时的标志。如果以"@"开头，则表示它是一个正则表达式，要进行正则匹配，否则只做普通字符串匹配。匹配到此标志之后，应返回 RX_RET_OK
 * @param failFlag 异常返回时的标志。如果以"@"开头，则表示它是一个正则表达式，要进行正则匹配，否则只做普通字符串匹配。匹配到此标志之后，应返回 RX_RET_FAIL
 * @return 一个 rx_ret_t 型整数，表示接收结束的原因。@see rx_ret_t
 */
rx_ret_t transceiver_cmdack(transceiver_t *trx, const char *cmd, char *rxbuf, size_t rxbuf_size, long timeout, const char *okFlag, const char *failFlag);

/**
 * @brief 使用 transceiver_t 向 console_t 发送文件
 */
std_err_t transceiver_send_file(transceiver_t *trx, const char *local_path, const char *remote_path, long timeout_ms, progress_callback_t cbprogress, void *cookie);

/**
 * @brief 使用 transceiver_t 从 console_t 接收文件
 */
std_err_t transceiver_recv_file(transceiver_t *trx, const char *local_path, long timeout_ms, progress_callback_t cbprogress, void *cookie);

/// @brief 设置做为标准 I/O 的收发器
/// @return 之前的收发器指针
transceiver_t* transceiver_set_std(transceiver_t* trx);

/// @brief 获取做为标准 I/O 的收发器
transceiver_t* transceiver_get_std();

#ifdef __cplusplus
} // extern "C"
#endif


#ifdef __cplusplus

/**
 * 使用 C++ 实现的 transceiver
 */
#include "utils.hpp"

class transceiver {
    private: transceiver_t _trx;
    public:  virtual transceiver_t* trx() { return &_trx; }
    private: static transceiver* tothis(struct _transceiver* trx) { return ((transceiver*)(trx->hif)); } // 把 transceiver_t.hif 强制转换为 transceiver*
    private: static size_t    _trx_get_mtu(struct _transceiver* trx) { return tothis(trx)->mtu(); }
    private: static std_err_t _trx_clear_rx_buffer(struct _transceiver *trx) { tothis(trx)->rxclear(); return STD_ERR_OK; }
    private: static rx_ret_t  _trx_rx(struct _transceiver *trx, void* buf, size_t size, size_t *plen, long timeout, /*long lead,*/ long interval, const char *okFlag, rx_flag_t okFlagType, const char *failFlag, rx_flag_t failFlagType) { return tothis(trx)->rx(buf, size, plen, timeout, 0, interval, okFlag, okFlagType, failFlag, failFlagType); }
    private: static std_err_t _trx_rxline(struct _transceiver *trx, void* buf, size_t size, size_t *plen)  { return tothis(trx)->rxline(buf, size, plen); }
    private: static std_err_t _trx_tx(struct _transceiver *trx, const void* buf, size_t size, size_t *plen) { return tothis(trx)->tx(buf, size, plen); }
    private: static void      _trx_close(struct _transceiver *trx) { delete tothis(trx); }
    private: static std_err_t _trx_listen(struct _transceiver *trx, bool enable) { return tothis(trx)->listen(enable); }
    public:  static std_err_t   rx_ret_to_std_err(rx_ret_t ret);
    public:  static const char* rx_ret_comment(rx_ret_t ret);
    
    // 正则匹配函数
    protected: regexp_match_t _regexp_match = NULL;
    // 当指令通道处于阻塞式接收情况下时，不回调 event_callback
    protected: bool _wait4ack = false;
    protected: transceiver(regexp_match_t regexp_match_func = NULL);
    public:    virtual ~transceiver() {}

    public: virtual size_t    mtu() = 0;
    public: virtual void      txclear() = 0;
    public: virtual void      rxclear() = 0;
    // 等待 RX 收到数据: timeout_ms < 0 时等待最长时间; timeout_ms = 0 时不等待立即返回; timeout_ms > 0 时等待指定时间后返回。有数据返回 STD_ERR_OK，无数据返回 STD_ERR_TIMEOUT。
    public: virtual std_err_t rxwait(long timeout_ms) = 0;
    public: virtual std_err_t rxread(void* buf, size_t size, size_t *plen/*out*/) = 0;
    /// 同步(阻塞)地发送数据
    public: virtual std_err_t txwrite(const void* buf, uint64_t size, size_t *plen/*out*/) = 0;
          
    public: virtual std_err_t tx(const void* buf, uint64_t size, size_t *plen/*out*/) { return txwrite(buf, size, plen); }
    public: virtual std_err_t tx(const char* str) { return txwrite(str, strlen(str), NULL); }
	public: virtual std_err_t rxline(void* buf, size_t size, size_t *plen);
    public: virtual rx_ret_t  rx(void* buf, size_t size, size_t *plen/*out*/, long total_timeout_ms, long lead_timeout_ms, long interval_timeout_ms, const void *ok_flag, rx_flag_t ok_flag_type, const void *fail_flag, rx_flag_t fail_flag_type);
    public: virtual rx_ret_t  cmdack(const char *cmd, char *rxbuf, size_t rxbuf_size, long timeout, const char *ok_flag, const char *fail_flag);
    public: virtual bool      iswait4ack() { return _wait4ack; }
	/// 是否进行异步事件监听: 要想使用 transceiver_t 的 on_rx_data/on_tx_empty 回调函数就必须启动监听
    public: virtual std_err_t listen(bool enable) = 0;
};

class transceiver_by_ring: public transceiver {
    protected: osfile* _ftxring = NULL;
    protected: osfile* _frxring = NULL;
    public:    transceiver_by_ring(osfile* frxring, osfile* ftxring, regexp_match_t regexp_match = NULL): transceiver(regexp_match) { _frxring = frxring; _ftxring = ftxring; }
    protected: virtual void      sync() = 0;
    public:    virtual size_t    mtu() override { return rxsize(); }

    protected: virtual uint64_t  txbase() = 0;
    protected: virtual uint64_t  txsize() = 0;
    protected: virtual uint64_t  txhead() = 0;
    protected: virtual uint64_t  txtail() = 0;
    protected: virtual void      txtail(uint64_t tail) = 0;
    protected: virtual bool      txfull() = 0;
    protected: virtual bool      txempty() { return txdlen() == 0; }
    protected: virtual uint64_t  txdlen()  { return txfull() ? txsize() : ((txsize() - txhead() + txtail()) % txsize()); }
    protected: virtual uint64_t  txfree()  { return txsize() - txdlen(); }
    public:    virtual void      txclear() = 0;
    public:    virtual std_err_t txwrite(const void* buf, uint64_t size, size_t *plen/*out*/);

    protected: virtual uint64_t  rxbase() = 0;
    protected: virtual uint64_t  rxsize() = 0;
    protected: virtual uint64_t  rxhead() = 0;
    protected: virtual void      rxhead(uint64_t head) = 0;
    protected: virtual uint64_t  rxtail() = 0;
    protected: virtual bool      rxfull() = 0;
    protected: virtual bool      rxempty() { return rxdlen() == 0; }
    protected: virtual uint64_t  rxdlen()  { return rxfull() ? rxsize() : ((rxsize() - rxhead() + rxtail()) % rxsize()); }
    protected: virtual uint64_t  rxfree()  { return rxsize() - rxdlen(); }
    public:    virtual void      rxclear() = 0;
    public:    virtual std_err_t rxread(void* buf, size_t size, size_t *plen/*out*/);
};

#endif
